package com.gem.factory.game;

import com.gem.entity.*;
import com.gem.enums.ClientMessageType;
import com.gem.enums.GameRoundStageType;
import com.gem.enums.GemType;
import com.gem.enums.MessageColorType;
import com.gem.factory.card.CardBaseFactory;
import com.gem.factory.log.LogFactory;
import com.gem.factory.room.RoomFactory;
import com.gem.factory.user.UserFactory;
import com.gem.netty.entity.CardInfo;
import com.gem.netty.entity.ClientContent;
import com.gem.netty.entity.CurrentUser;
import com.gem.netty.delay.SkipRound;
import com.gem.netty.message.robot.AddRobot;
import com.gem.netty.message.robot.RobotRound;
import com.gem.netty.session.GroupSessionFactory;
import com.gem.utils.R;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * Author: Xhy
 * CreateTime: 2022-09-21 18:48
 * 游戏工厂
 */
@Component
public class GameFactory {

    // key: 房间id ,value : 游戏信息
    private static ConcurrentHashMap<String, Game> games = new ConcurrentHashMap<>();

    // key: 房间id , value :房间内的用户
    private static ConcurrentHashMap<String,List<CurrentUser>> gameInUsers = new ConcurrentHashMap<>();

    /**
     * 加入游戏
     * @param roomId
     * @param game
     */
    public static void put(String roomId,Game game){
        games.put(roomId,game);
    }

    /**
     * 获取游戏
     * @param roomId
     * @return
     */
    public static Game get(String roomId){
        return games.get(roomId);
    }

    /**
     * 删除游戏
     * @param roomId
     */
    public static void remove(String roomId){
        games.remove(roomId);
    }

    /**
     * 开始游戏
     * @param roomId
     */
    public static R start(String roomId){
        // 房间是否存在
        if (!RoomFactory.existRoom(roomId)){
            return R.error().message("房间不存在");
        }
        // 是否满足开始游戏要求 member >= 2
        if (!RoomFactory.existStartGame(roomId)){
            return R.error().message("至少2人才能开始游戏");
        }

        // 初始化游戏
        Game game = init(roomId);


        return R.ok().data("data",game);

    }

    private static Game init(String roomId){

        Set<User> members = RoomFactory.getRoomInMembers(roomId);
        int numbers = members.size();
        Game game = new Game();

        // 修改房间状态
        RoomFactory.get(roomId).setState(true);
        // 初始化卡牌
        Map<Integer, List<Card>> cards = Card.initCards();

        Map<Integer, List<Card>> showCards = new HashMap<>();
        showCards.put(1,new ArrayList<>());
        showCards.put(2,new ArrayList<>());
        showCards.put(3,new ArrayList<>());
        // 抽取各等级4张牌到场上牌组
        cards.forEach((k,v)->{
            List<Card> cardList = cards.get(k);
            for (int j = 0; j < 4; j++) {
                showCards.get(k).add(cardList.get(j));
                cardList.remove(j);
            }
        });
        CardBaseFactory.put(roomId,cards);
        game.setShowCards(showCards);
        // 初始化宝石
        Map<Integer, Integer> gems = Gem.initGems(numbers);
        game.setGems(gems);

        // 初始化贵族
        List<Nobility> nobilities = Nobility.init(numbers);

        game.setNobilities(nobilities);

        // 当前玩家下标
        game.setCurrentIndex(0);
        // 当前回合序列号
        game.setCurrentUserNo(0);
        game.setRoomId(roomId);

        // 依靠玩家获得的分数排序
        game.setWins(new PriorityQueue<>(new winScore()));

        // 初始化游戏信息
        List<CurrentUser> users = new ArrayList<>();

        for (User user : members.stream().collect(Collectors.toList())) {
            CurrentUser gameUser = new CurrentUser();
            String uId = user.getId();
            gameUser.setId(uId);
            gameUser.setName(user.getName());
            gameUser.setGems(initUserGems());
            gameUser.setCards(initUserCardsInfo());
            gameUser.setCurrentGemState(new HashMap<>());
            gameUser.setState(GameRoundStageType.WAIT.type);
            gameUser.setGetGemNumber(0);
            gameUser.setCurrentGetCardInfo(new CardInfo());
            gameUser.setOwnScore(0);
            gameUser.setTSFlag(false);
            gameUser.setTSCards(new ArrayList<>());
            gameUser.setType(user.getType());
            if (!ObjectUtils.isEmpty(user.getRobotLevel())){
                gameUser.setRobotLevel(user.getRobotLevel());
            }
            users.add(gameUser);
        }
        // 排序
        Collections.shuffle(users);
        game.setUsers(users);
        // 先让人类是房主 TODO
        for (CurrentUser user : users) {
            if (user.getType()) {

            }
        }
        CurrentUser roomOwner = game.getUsers().get(0);
        roomOwner.setState(GameRoundStageType.GET_GEM.type);
        game.setUserId(roomOwner.getId());
        RoomFactory.get(roomId).setState(true);
        GameFactory.put(roomId,game);
        // 房主可能是机器人
        if (roomOwner.getType() == false){
            ClientContent content = new ClientContent();
            content.setType(ClientMessageType.ADD_ROBOT.type);
            content.setSendId(game.getUserId());
            RobotRound.RobotRound(roomOwner,game);
        }
        return game;
    }

    private static TreeMap<Integer, Integer> initUserGems() {
        TreeMap<Integer, Integer> userGems = new TreeMap<>();
        userGems.put(GemType.BLUE.type,0);
        userGems.put(GemType.WHITE.type,0);
        userGems.put(GemType.GOLD.type,0);
        userGems.put(GemType.GREEN.type,0);
        userGems.put(GemType.BLACK.type,0);
        userGems.put(GemType.RED.type,0);
        return userGems;
    }

    public static boolean exist(String roomId) {
        return games.containsKey(roomId);
    }

    static class winScore implements Comparator<CurrentUser>{

        @Override
        public int compare(CurrentUser o1, CurrentUser o2) {
            return o1.getOwnScore() - o2.getOwnScore();
        }
    }
    private static TreeMap<Integer, List<Card>> initUserCardsInfo(){
        TreeMap<Integer, List<Card>> cards = new TreeMap<>();
        cards.put(GemType.BLUE.type,new ArrayList<>());
        cards.put(GemType.GOLD.type,new ArrayList<>());
        cards.put(GemType.BLACK.type,new ArrayList<>());
        cards.put(GemType.WHITE.type,new ArrayList<>());
        cards.put(GemType.GREEN.type,new ArrayList<>());
        cards.put(GemType.RED.type,new ArrayList<>());
        return cards;
    }

    /**
     * 游戏开始时调用的方法,初始化
     * @param roomId
     */
    public static void begin(String roomId){
        Game game = GameFactory.get(roomId);
        game.setCurBeginTime(System.currentTimeMillis());
        // 监听当前用户当前回合时长
        SkipRound.getInstance().add(game,60);
        // 修改当前用户为拿宝石阶段
        CurrentUser user = game.getUsers().get(game.getCurrentIndex());
        String userName = user.getName();
        setGetGem(user);
        List<Channel> channels  = GroupSessionFactory.getGroupSession().getMembersChannel(game.getRoomId());
        GameFactory.notifyUsers(channels,R.ok().data("color", MessageColorType.SYSTEM_MESSAGE.type).message(userName  +" 玩家阶段").type(ClientMessageType.ROUND_BEGAN.type),roomId);

    }
    /**
     * 回合给下一个用户
     */
    private static void setUserRound(Game game){
        int currentUserIndex = (game.getCurrentIndex()+1) % game.getUsers().size();
        game.setCurrentIndex(currentUserIndex);
        game.setUserId(game.getUsers().get(currentUserIndex).getId());
        // 修改状态
        game.getUsers().get(currentUserIndex).setState(GameRoundStageType.WAIT.type);
    }

    // 跳过回合
    public static void roundStage(String roomId){
        Game game = GameFactory.get(roomId);
        if(game!=null){
            // 添加当前回合时间戳
            game.setCurBeginTime(System.currentTimeMillis());
            // 删除延迟队列中记录之前用户的记录
            SkipRound.getInstance().remove(game);
            // 监听当前用户当前回合时长
            SkipRound.getInstance().add(game,60);

            List<Channel> channels = null;
            // 是否有获胜玩家
            if (tryWin(game)){
                // 多个玩家 >= 15分的情况下选出分最大的
                CurrentUser user = game.getWins().poll();
                channels = GroupSessionFactory.getGroupSession().getMembersChannel(game.getRoomId());
                GameFactory.notifyUsers(channels,R.ok().data("color", MessageColorType.SYSTEM_MESSAGE.type).message(UserFactory.get(user.getId() + " 获得了胜利,得分:" + user.getOwnScore())).type(ClientMessageType.GAME_END.type),roomId);
                return;
            }
            // 通知其他玩家不可操作
            channels = GroupSessionFactory.getGroupSession().getMembersChannel(game.getRoomId());
            setUserRound(game);
            game.setCurrentUserNo(game.getCurrentUserNo()+1);
            // 修改当前用户为拿宝石阶段
            CurrentUser user = game.getUsers().get(game.getCurrentIndex());
            user.setCurrentGetCardInfo(new CardInfo());
            user.setCurrentGetGemInfo(0);
            user.setCurrentGetNobilityInfo(new Nobility());
            user.setCurrentGemState(new HashMap<>());
            user.setTSFlag(false);
            String userName = user.getName();
            setGetGem(user);
            Boolean userType = user.getType();
            String message = userType ? userName  +" 玩家阶段" : userName+ " 阶段";
            GameFactory.notifyUsers(channels,R.ok()
                    .data("color", MessageColorType.SYSTEM_MESSAGE.type).message(message).type(ClientMessageType.ROUND_BEGAN.type),roomId);
            // 机器人/人类回合
            if (!userType){
                RobotRound.RobotRound(user,game);
            }
            GameFactory.tryMakeUpGem(game);
        }

    }

    private static void setGetGem(CurrentUser user) {
        user.setState(GameRoundStageType.GET_GEM.type);
    }


    // 是否有胜利的玩家
    private static boolean tryWin(Game game) {
        // 判断下一个玩家是否是新的回合,当前索引处 +1 % 房间内的玩家 == 0 说明下一个回合为新的回合
        if (game.getWins().size() > 0 && (game.getCurrentIndex() +1) % game.getUsers().size() == 0){
            return true;
        }
        return false;
    }

    // 获取房间内的用户
    public static List<CurrentUser> getUsers(String roomId){
        return gameInUsers.get(roomId);
    }

    /**
     * 卡牌库存库减少
     * @param game
     * @param content
     */
    public static void reduceGameCard(ClientContent content,Game game) {
        // 获取用户拿取的牌
        CardInfo cardInfo = content.getContent().getCardInfo();
        List<Card> cardList = game.getShowCards().get(cardInfo.getType());
        // 减库
        int i = 0;
        // 是否是压的牌
        CurrentUser user = game.getUsers().get(game.getCurrentIndex());
            for (; i < cardList.size(); i++) {
                if (cardList.get(i).getNo() == cardInfo.getNo()){
                    break;
                }
            }
            // 清库
            cardList.remove(i);
    }



    public static void notifyUsers(List<Channel> channels,R r,String roomId){

        // 告知其他用户
        for (Channel channel : channels) {
            channel.writeAndFlush(new TextWebSocketFrame(r.toString()));
        }
        Log log = new Log();
        if (r.getData().containsKey("color")){
            log.setMessageColor(Integer.parseInt(r.getData().get("color").toString()));
        }
        log.setMessage(r.getMessage());
        log.setDate(new Date());
        LogFactory.add(roomId,log);
    }

    public static void getShowCard(Game game,ClientContent content) {
        // 获取牌库
        Map<Integer, List<Card>> cards = CardBaseFactory.get(game.getRoomId());
        // 获取用户拿取的牌类型
        CardInfo cardInfo = content.getContent().getCardInfo();
        Integer cardInfoType = cardInfo.getType();
        // 换牌
        List<Card> cardList = cards.get(cardInfoType);
        game.getShowCards().get(cardInfoType).add(cardList.get(0));
        cardList.remove(0);
    }

    public static void tryMakeUpGem(Game game) {
        // 获取所有用户
        List<CurrentUser> users = game.getUsers();
        // 获取场上宝石
        Map<Integer, Integer> showGems = game.getGems();
        int[] gems = new int[7];
        // 计算场上宝石和用户拥有的宝石
        for (CurrentUser user : users) {
            user.getGems().forEach((type,number)->{
                gems[type]+=number;
            });
        }
        showGems.forEach((type,number)->{
            gems[type]+=number;
        });
        // 用应该的宝石数量 - 计算后的值 +=场上的宝石
        int numbers = users.size();
        int limit = numbers == 2 ? 4 : numbers == 3 ? 5: 7;
        int goldLimit = 5;
        for (int i = 1; i < gems.length-1; i++) {
            showGems.put(i,showGems.get(i)+(limit - gems[i]));
        }
        showGems.put(GemType.GOLD.type,showGems.get(GemType.GOLD.type)+ (goldLimit - gems[GemType.GOLD.type]));
    }
}
